package com.blockmeta.bbs.immodule.service;

import android.app.Notification;
import android.app.Service;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.RemoteException;

import com.blankj.utilcode.util.LogUtils;
import com.blankj.utilcode.util.Utils;
import com.blockmeta.bbs.baselibrary.base.app.BaseApp;
import com.blockmeta.bbs.baselibrary.net.Host;
import com.blockmeta.bbs.baselibrary.provider.IApplicationProvider;
import com.blockmeta.bbs.businesslibrary.BuildConfig;
import com.blockmeta.bbs.businesslibrary.arouter.RouterHelper;
import com.blockmeta.bbs.businesslibrary.arouter.RouterPath;
import com.blockmeta.bbs.businesslibrary.pojo.UserPhonePOJO;
import com.blockmeta.bbs.businesslibrary.util.AccountHelper;
import com.blockmeta.bbs.immodule.ClientService;
import com.blockmeta.bbs.immodule.Constants;
import com.blockmeta.bbs.immodule.ICallBack;
import com.blockmeta.bbs.immodule.SessionService;
import com.blockmeta.bbs.immodule.bean.ClientRequestMessage;
import com.blockmeta.bbs.immodule.bean.MsgHelper;
import com.blockmeta.bbs.immodule.bean.Params;
import com.blockmeta.bbs.immodule.bean.databean.ClientRequestBean;
import com.blockmeta.bbs.immodule.bean.databean.DeleteUnReadRequestBean;
import com.blockmeta.bbs.immodule.dao.ChatMessage;
import com.google.gson.Gson;

import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

import buf.Msg;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.protobuf.ProtobufDecoder;
import io.netty.handler.codec.protobuf.ProtobufEncoder;
import io.netty.handler.codec.protobuf.ProtobufVarint32FrameDecoder;
import io.netty.handler.codec.protobuf.ProtobufVarint32LengthFieldPrepender;
import io.netty.handler.codec.serialization.ClassResolvers;
import io.netty.handler.codec.serialization.ObjectDecoder;
import io.netty.handler.timeout.IdleStateHandler;

public class Session extends Service {
    public static final String CONNECTIVITY_CHANGE_ACTION = "android.net.conn.CONNECTIVITY_CHANGE";
    private UserPhonePOJO user;
    private String sessionToken;
    private SocketChannel socketChannel;
    private EventLoopGroup eventLoopGroup = new NioEventLoopGroup();
    private Bootstrap bootstrap;
    private NettyClientHandler nettyClientHandler;
    private JsonDataHandler jsonDataHandler;
    private ListMessageHandler listMessageHandler;
    protected Handler m_handler;

    private ClientService imClientService;
    private ICallBack iCallBack;

    public int totalRetryTime = 0;
    public int totalRetryLoginTime = 0;

    private static final int MSG_DELAY_CLOSE = 0x01; // 延迟关闭
    private AtomicInteger mCurrentStatus = new AtomicInteger(SocketStatus.DISCONNECTED);

    private AtomicInteger pingTimes = new AtomicInteger(0);


    @Override
    public void onCreate() {
        super.onCreate();
        Utils.init(getApplication());
        if (!BuildConfig.DEBUG) {
            //如果为正式环境，不输出socketlog
            LogUtils.getConfig().setLogSwitch(false);
        }
//        bindService();

    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        LogUtils.i("authorkkk" + "Session onDestroy");
        // 注销监听
        closeConnection(false);
    }

    @Override
    public IBinder onBind(Intent intent) {
        return new LocalBinder();
    }


    /**
     * TODO 只有StartService才会走这个方法
     * TODO 如果是不同进程，涉及进程间通讯问题！
     */
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
//        this.serverIp = "47.111.26.13";// intent.getStringExtra(Constants.INTENT_EXTRA_SERVER_IP);
//        this.serverPort = 9989;// intent.getIntExtra(Constants.INTENT_EXTRA_SERVER_IP,


        if (intent == null || intent.getExtras() == null || !intent.getExtras().containsKey(Params.Seriliza_Token)) {
            LogUtils.i("authorkkk" + "错误杀死APP接触Service二次绑定");
            return Service.START_NOT_STICKY;
        }

        nettyClientHandler = new NettyClientHandler(Session.this, pingTimes);
        jsonDataHandler = new JsonDataHandler(Session.this);
        listMessageHandler = new ListMessageHandler(Session.this);
        m_handler = new InternalHandler();

        user = intent.getParcelableExtra(Params.Seriliza_UserPOJO);
        sessionToken = intent.getStringExtra(Params.Seriliza_Token);
        boolean appBackGround = intent.getBooleanExtra(Params.APPBACKGROUND, false);
        if (appBackGround) {
            startForeground(111, new Notification());
        }
        LogUtils.i("authorkkk" + "获取用户消息:" + user.getUserName() + "token:" + sessionToken);
        if (user != null && sessionToken != null) {

            // 启动通讯服务
            buildConnect();
        }
        return Service.START_REDELIVER_INTENT;

    }

    private class InternalHandler extends Handler {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case MSG_DELAY_CLOSE:
                    closeConnection(false);
                    break;
            }
        }
    }

    private synchronized void buildConnect() {
        switch (getCurrentStatus()) {
            case SocketStatus.CONNECTED:
            case SocketStatus.CONNECTING:
                break;
            default:
                setCurrentStatus(SocketStatus.CONNECTING);
                connect();
        }
    }

    public synchronized int getCurrentStatus() {
        return mCurrentStatus.get();
    }

    public ICallBack getIMCallBack() {
        return iCallBack;
    }

    public synchronized void setCurrentStatus(int currentStatus) {
        this.mCurrentStatus.set(currentStatus);
    }

    public ClientService getIMClientService() {
        if (imClientService == null) {
            if (connection != null) BaseApp.getApp().unbindService(connection);
            bindService();
        }
        return imClientService;
    }

    private ServiceConnection connection = new ServiceConnection() {

        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            LogUtils.d("imclient service connected");
            imClientService = ClientService.Stub.asInterface(service);
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            LogUtils.d("imclient service disconnected");
            imClientService = null;
        }

    };

    private void bindService() {
        IApplicationProvider provider = RouterHelper.getProvider(RouterPath.APPLICATION_PROVIDER);
        Intent mClientIntent = new Intent(Constants.INTENT_SERVICE_IMCLIENTSERVICE);
        mClientIntent.setPackage(provider.getProjectBuildInfo().application_id);
        BaseApp.getApp().bindService(mClientIntent, connection, Context.BIND_AUTO_CREATE);
    }

    /**
     * 发送删除服务端离线消息
     */
    public void sendDeleteUnliveMessage(long id) {
        DeleteUnReadRequestBean bean = new DeleteUnReadRequestBean();
        bean.setId(id);
        Msg.Message msg = MsgHelper.newClientRequestMessage(
                ClientRequestMessage.DELETE_UNREAD_MSG, user.getUid(),
                sessionToken, new Gson().toJson(bean));
        write2Socket(msg);
    }


    public class LocalBinder extends SessionService.Stub {

        public Session getService() {
            return Session.this;
        }


        @Override
        public void sendMessage(String uuid, int contentType, String message
                , long toId, int msgType, String filePath, String fileName, int fileType
                , String fileParam, String userName, String userAvator, int groupId) throws RemoteException {
            Msg.Message msg = null;

            switch (msgType) {
                case ChatMessage.MSG_TYPE_UU://人人聊天
                    msg = MsgHelper.newUUChatMessage(uuid, user.getUid(), toId,
                            message, sessionToken, true,
                            (int) (System.currentTimeMillis() / 1000L), 0, contentType,
                            filePath, ChatMessage.STATUS_SEND, fileName, fileType, fileParam
                            , userName, userAvator, groupId);
                    break;
                case ChatMessage.MSG_TYPE_UCG://群聊 toid==chatGroupId
                    msg = MsgHelper.newUCGChatMessage(uuid, user.getUid(), toId,
                            message, sessionToken, true,
                            (int) (System.currentTimeMillis() / 1000L), 0, contentType,
                            filePath, ChatMessage.STATUS_SEND, fileName, fileType, fileParam
                            , userName, userAvator, groupId);
                    break;
            }
            LogUtils.i("authorkkk" + "MSG:" + msg.toString());
            write2Socket(msg);
//            BroadcastHelper.onSendChatMessage(Session.this);
        }

        @Override
        public void getFriendList() throws RemoteException {
            LogUtils.i("authorkkk" + "getFriendList" + user.getUid());
            Msg.Message msg = MsgHelper.newClientRequestMessage(
                    ClientRequestMessage.FRIEND_LIST, user.getUid(), sessionToken, "");
            write2Socket(msg);
        }

        @Override
        public void getMessageList(long fromMessageId) throws RemoteException {
            LogUtils.i("authorkkk" + "getMessageList uid" + user.getUid() + " session" + sessionToken);
            ClientRequestBean clientRequestBean = new ClientRequestBean();
            clientRequestBean.setDevice("android");
            clientRequestBean.setId(fromMessageId);
            Msg.Message msg1 = MsgHelper.newClientRequestMessage(
                    ClientRequestMessage.CHAT_MESSAGE_LIST, user.getUid(),
                    sessionToken, new Gson().toJson(clientRequestBean));

            write2Socket(msg1);
        }

        @Override
        public int getUserId() throws RemoteException {
            return user.getUid();
        }

        @Override
        public String getUserName() throws RemoteException {
            return user.getUserName();
        }

        @Override
        public String getToken() throws RemoteException {
            return sessionToken;
        }

        @Override
        public void setCallBack(ICallBack callback) throws RemoteException {
            iCallBack = callback;
            LogUtils.i("authorkkk" + "setCallBack "  + callback.toString());
        }


        @Override
        public void getTodoList(int fromMessageId) throws RemoteException {
            LogUtils.i("authorkkk" + "getTodoList" + user.getUid());
            Msg.Message msg1 = MsgHelper.newClientRequestMessage(
                    ClientRequestMessage.TODO_LIST, user.getUid(), sessionToken,
                    fromMessageId + "");
            write2Socket(msg1);
        }


        @Override
        public void getChatGroupMemberList(int groupId) throws RemoteException {
            LogUtils.i("authorkkk" + "getChatGroupMemberList" + groupId + " " + user.getUid());
            Msg.Message msg = MsgHelper.newClientRequestMessage(
                    ClientRequestMessage.CHAT_GROUP_MEMBER_LIST, user.getUid(),
                    sessionToken, "" + groupId);
            write2Socket(msg);

        }

        @Override
        public void getChatGroupList() throws RemoteException {
            LogUtils.i("authorkkk" + "getChatGroupList" + user.getUid());
            Msg.Message msg = MsgHelper.newClientRequestMessage(
                    ClientRequestMessage.CHAT_GROUP_LIST, user.getUid(), sessionToken,
                    "");
            write2Socket(msg);
        }


    }

    private void write2Socket(Msg.Message msg) {
        if (socketChannel != null) {
            socketChannel.writeAndFlush(msg);
        }
    }

    /**
     * 利用netty连接远程服务端保持通讯
     */
    private void connect() {
        LogUtils.i("authorkkk" + "connect");
        if (socketChannel != null) {
            LogUtils.i("authorkkk" + "connect" + socketChannel.isOpen() + " " + socketChannel.isActive());
        }
        bootstrap = new Bootstrap();
        bootstrap.channel(NioSocketChannel.class);
        bootstrap.option(ChannelOption.SO_KEEPALIVE, true);
        bootstrap.group(eventLoopGroup);
        bootstrap.remoteAddress(getIp(), getPort());
        LogUtils.i("authorkkk" + "IP:" + getIp() + "Port:" + getPort());
        bootstrap.handler(new ChannelInitializer<SocketChannel>() {
            @Override
            protected void initChannel(SocketChannel socketChannel)
                    throws Exception {

                List<String> names = socketChannel.pipeline().names();
                for (String name : names) {
                    LogUtils.d("authorkkk" + "already has handler" + names);
                }

                IdleStateHandler idleStateHandler = new IdleStateHandler(200, 30, 0, TimeUnit.SECONDS);
                socketChannel.pipeline().addLast("IdleStateHandler",
                        idleStateHandler);


                socketChannel.pipeline().addLast(
                        new ProtobufVarint32FrameDecoder());
                socketChannel.pipeline().addLast("ProtobufDecoder",
                        new ProtobufDecoder(Msg.Message.getDefaultInstance()));
                // encoded
                socketChannel.pipeline().addLast("LengthFieldPrepender",
                        new ProtobufVarint32LengthFieldPrepender());
                socketChannel.pipeline().addLast("ProtobufEncoder",
                        new ProtobufEncoder());

                socketChannel.pipeline().addLast("ClassResolvers",
                        new ObjectDecoder(ClassResolvers.cacheDisabled(null)));
                socketChannel.pipeline().addLast(nettyClientHandler);
                socketChannel.pipeline().addLast(jsonDataHandler);
                socketChannel.pipeline().addLast(listMessageHandler);

            }
        });

        try {
            ChannelFuture future = bootstrap.connect(getIp(), getPort());
            future.addListener(new ChannelFutureListener() {
                @Override
                public void operationComplete(ChannelFuture future) throws Exception {
                    // 登录认证
                    if (future.isSuccess()) {
                        setCurrentStatus(SocketStatus.CONNECTED);
                        LogUtils.i("authorkkk" + "make new im connectconnectok");
                        socketChannel = (SocketChannel) future.channel();
                        auth();
                    } else {
                        setCurrentStatus(SocketStatus.FAIL);
                        LogUtils.i("authorkkk" + "make new im connectconnectfail");
                        reConnect();
                    }
                }
            });
            // future.channel().closeFuture().sync();
        } catch (Exception e) {
            LogUtils.i("authorkkk" + "make new im connectconnectfailexception" + e.getMessage());
            e.printStackTrace();
            setCurrentStatus(SocketStatus.FAIL);
            reConnect();
        }
    }


    private int getPort() {
        return 9950;
    }

    //先写死多服务器的时候后面改成域名
    private String getIp() {
        //test env
        return Host.BASE_URL.contains("quhui123")? "47.114.37.19" : "192.168.1.38";
    }

    public synchronized void connectNextServer() {

        if (getCurrentStatus() != SocketStatus.CONNECTED)    // Standby狀態不重新連線
        {
            m_handler.removeCallbacksAndMessages(null);
            m_handler.postDelayed(new Runnable() {
                @Override
                public void run() {
                    LogUtils.d("author" + "connectNextServer");
                    //如果网络是好的直接重连，如果没有网络就循环请求该方法直到有网络
                    m_handler.removeCallbacksAndMessages(null);
                    if (getCurrentStatus() != SocketStatus.CONNECTED) {
                        if (isNetworkConnected(Session.this)) {
                            totalRetryTime++;
                            closeConnection(true);
                        } else {
                            connectNextServer();
                        }
                    }
                }
            }, totalRetryTime == 0 ? 2000 : (totalRetryTime / 5 * 2000));
        }
    }

    /**
     * 发送读空闲失败时，进行重连
     *
     * @return
     */
    public synchronized void reConnect() {
        LogUtils.i("authorkkk" + "reConnect");
        totalRetryTime = 0;
        totalRetryLoginTime = 0;
        connectNextServer();
    }


    //检查网络是否连接
    private boolean isNetworkConnected(Context context) {
        if (context != null) {
            ConnectivityManager mConnectivityManager = (ConnectivityManager) context
                    .getSystemService(Context.CONNECTIVITY_SERVICE);
            NetworkInfo mNetworkInfo = mConnectivityManager
                    .getActiveNetworkInfo();
            if (mNetworkInfo != null) {
                LogUtils.i("authorkkk" + "网络正常");
                return mNetworkInfo.isAvailable();
            }
        }
        return false;
    }

    private synchronized void closeConnection(final boolean reconnect) {
        if (socketChannel == null) {
            if (reconnect) {
                buildConnect();
            }
            return;
        }
        IdleStateHandler idleStateHandler = socketChannel.pipeline().get(IdleStateHandler.class);
        if (idleStateHandler != null) {
            socketChannel.pipeline().remove(IdleStateHandler.class);
        }
        NettyClientHandler nettyClientHandler = socketChannel.pipeline().get(NettyClientHandler.class);
        if (nettyClientHandler != null) {
            socketChannel.pipeline().remove(NettyClientHandler.class);
        }
        ChannelFuture future = socketChannel.close();
        future.addListener(new ChannelFutureListener() {
            @Override
            public void operationComplete(ChannelFuture future) throws Exception {
                if (future.isSuccess()) {
                    setCurrentStatus(SocketStatus.DISCONNECTED);
                    LogUtils.i("authorkkk" + "netty 成功关闭，准备重启!");
                } else {
                    LogUtils.i("authorkkk" + "netty 关闭失败！");
                }
                if (reconnect) {
                    buildConnect();
                }
            }
        });
    }


    /**
     * 登陆服务器
     */
    public void auth() {
        //如果认证失败超过n次，不再重新认证
        if (totalRetryLoginTime > 3) {
            return;
        }
        totalRetryLoginTime++;
        Msg.Message loginMsg = Msg.Message
                .newBuilder()
                .setClientLoginMessage(
                        Msg.ClientLoginMessage.newBuilder().setToken(sessionToken)
                                .setClientId(AccountHelper.id(this))
                                .setClientType("android")
                                .setUserId(user.getUid()).build())
                .setMessageType(Msg.MessageType.CLIENT_LOGIN).build();
        write2Socket(loginMsg);
        LogUtils.i("authorkkk" + "启动登陆认证token" + sessionToken + " userid:" + user.getUid() + " clientid:" + AccountHelper.id(this));
    }


    public UserPhonePOJO getUser() {
        return user;
    }

    public String getSessionToken() {
        return sessionToken;
    }


    @Override
    public boolean onUnbind(Intent intent) {
        LogUtils.i("authorkkk" + "Session unbindService");
        return super.onUnbind(intent);
    }

}
